package com.codeanalysissys.backend.javascript;

import com.codeanalysissys.backend.graphs.cfg.CFEdge;
import com.codeanalysissys.backend.graphs.cfg.CFNode;
import com.codeanalysissys.backend.graphs.cfg.ControlFlowGraph;
import com.codeanalysissys.backend.javascript.parser.JavaScriptBaseVisitor;
import com.codeanalysissys.backend.javascript.parser.JavaScriptLexer;
import com.codeanalysissys.backend.javascript.parser.JavaScriptParser;
import ghaffarian.graphs.Edge;
import ghaffarian.nanologger.Logger;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;

import java.io.*;
import java.util.*;

public class JavaScriptCFGBuilder {

    public static ControlFlowGraph build(InputStream inFile) throws IOException {
        // 将字符串转换为字节数组
//        byte[] bytes = content.getBytes();
        // 使用 ByteArrayInputStream 封装字节数组为输入流
//        InputStream inFile = new ByteArrayInputStream(bytes);

        ANTLRInputStream input = new ANTLRInputStream(inFile);
        JavaScriptLexer lexer = new JavaScriptLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        JavaScriptParser parser = new JavaScriptParser(tokens);
        ParseTree tree = parser.program();
        return build(tree, null, null);
    }

    /**
     * ‌Build and return the Control Flow Graph (CFG) for the given Java source file.
     */
    public static ControlFlowGraph build(String jsFile) throws IOException {
        return build(new File(jsFile));
    }

    /**
     * ‌Build and return the Control Flow Graph (CFG) for the given Java source file.
     */
    public static ControlFlowGraph build(File jsFile) throws IOException {
        if (!jsFile.getName().endsWith(".js"))
            throw new IOException("Not a JavaScript File!");
        InputStream inFile = new FileInputStream(jsFile);
        ANTLRInputStream input = new ANTLRInputStream(inFile);
        JavaScriptLexer lexer = new JavaScriptLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        JavaScriptParser parser = new JavaScriptParser(tokens);
        ParseTree tree = parser.program();
        return build(jsFile.getName(), tree, null, null);
    }

    /**
     * build with file without propKey
     * @param fileName
     * @param inputStream
     * @return
     * @throws IOException
     */
    public static ControlFlowGraph build(String fileName, InputStream inputStream) throws IOException {

        InputStream inFile = inputStream;
        ANTLRInputStream input = new ANTLRInputStream(inFile);
        JavaScriptLexer lexer=new JavaScriptLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        JavaScriptParser parser=new JavaScriptParser(tokens);
        ParseTree tree = parser.program();
        return build(fileName, tree, null, null);
    }

    /**
     * ‌Build and return the Control Flow Graph (CFG) for the given Parse-Tree.
     * The 'ctxProps' map includes contextual-properties for particular nodes
     * in the parse-tree, which can be used for linking this graph with other
     * graphs by using the same parse-tree and the same contextual-properties.
     */
    public static ControlFlowGraph build(String jsFileName, ParseTree tree,
                                              String propKey, Map<ParserRuleContext, Object> ctxProps) {
        ControlFlowGraph cfg = new ControlFlowGraph(jsFileName);
        JavaScriptCFGBuilder.ControlFlowVisitor visitor = new JavaScriptCFGBuilder.ControlFlowVisitor(cfg, propKey, ctxProps);
        visitor.visit(tree);
        return cfg;
    }

    /**
     * build without file
     * @param tree
     * @param propKey
     * @param ctxProps
     * @return
     */
    public static ControlFlowGraph build(ParseTree tree, String propKey, Map<ParserRuleContext, Object> ctxProps) {
        ControlFlowGraph cfg = new ControlFlowGraph();
        JavaScriptCFGBuilder.ControlFlowVisitor visitor = new JavaScriptCFGBuilder.ControlFlowVisitor(cfg, propKey, ctxProps);
        visitor.visit(tree);
        return cfg;
    }

    /**
     * Visitor-class which constructs the CFG by walking the parse-tree.
     */
    private static class ControlFlowVisitor extends JavaScriptBaseVisitor<Void> {

        private ControlFlowGraph cfg;
        private Deque<CFNode> preNodes;
        private Deque<CFEdge.Type> preEdges;
        private Deque<Block> loopBlocks;
        private List<Block> labeledBlocks;
        private Deque<Block> tryBlocks;
        private Queue<CFNode> casesQueue;
        private boolean dontPop;
        private String propKey;
        private Map<ParserRuleContext, Object> contexutalProperties;
        private Deque<String> classNames;

        public ControlFlowVisitor(ControlFlowGraph cfg, String propKey, Map<ParserRuleContext, Object> ctxProps) {
            // 初始化各种数据结构
            preNodes = new ArrayDeque<>();
            preEdges = new ArrayDeque<>();
            loopBlocks = new ArrayDeque<>();
            labeledBlocks = new ArrayList<>();
            tryBlocks = new ArrayDeque<>();
            casesQueue = new ArrayDeque<>();
            classNames = new ArrayDeque<>();
            dontPop = false;
            this.cfg = cfg;
            //
            this.propKey = propKey;
            contexutalProperties = ctxProps;
        }

        /**
         * Reset all data-structures and flags for visiting a new method declaration.
         */
        private void init() {
            preNodes.clear();
            preEdges.clear();
            loopBlocks.clear();
            labeledBlocks.clear();
            tryBlocks.clear();
            dontPop = false;
        }

        /**
         * Add contextual properties to the given node.
         * This will first check to see if there is any property for the
         * given context, and if so, the property will be added to the node.
         */
        private void addContextualProperty(CFNode node, ParserRuleContext ctx) {
            if (propKey != null && contexutalProperties != null) {
                Object prop = contexutalProperties.get(ctx);
                if (prop != null)
                    node.setProperty(propKey, prop);
            }
        }


//        @Override public Void visitProgram(JavaScriptParser.ProgramContext ctx) {
//           //program
//            //    : HashBangLine? sourceElements? EOF
//            System.out.println("进入visit=================");
//            cfg.setPackage(ctx.sourceElements().getText());
//            visitSourceElements(ctx.sourceElements());
//            return null;
//        }
//
//
//        @Override public Void visitSourceElements(JavaScriptParser.SourceElementsContext ctx) {
//            for (JavaScriptParser.SourceElementContext sourceElementContext : ctx.sourceElement()) {
//                visitSourceElement(sourceElementContext);
//            }
//            return null;
//        }
//
//
//        @Override public Void visitSourceElement(JavaScriptParser.SourceElementContext ctx) {
//            if (ctx==null||ctx.isEmpty()){
//                return null;
//            }
//            System.out.println("进入sourceElement======");
//            return visitStatement(ctx.statement());
//        }
//
//
//        @Override public Void visitStatement(JavaScriptParser.StatementContext ctx) {
//            if(ctx==null||ctx.isEmpty()){
//                return null;
//            }
//            String res=null;
//            if (ctx.block()!=null&&!ctx.block().isEmpty()){
//                System.out.println("block");
//                return visitBlock(ctx.block());
//            }else if(ctx.importStatement()!=null&&!ctx.importStatement().isEmpty()) {
//                System.out.println("import");
//                return visitImportStatement(ctx.importStatement());
//            }else if (ctx.variableStatement()!=null&&!ctx.variableStatement().isEmpty()){
//                System.out.println("variable");
//                return  visitVariableStatement(ctx.variableStatement());
//            }else if (ctx.exportStatement()!=null&&!ctx.exportStatement().isEmpty()){
//                System.out.println("export");
//                return  visitExpressionStatement(ctx.expressionStatement());
//            } else if (ctx.emptyStatement_()!=null&&!ctx.emptyStatement_().isEmpty()){
//                System.out.println("empty");
//                return  visitEmptyStatement_(ctx.emptyStatement_());
//            }else if (ctx.classDeclaration()!=null&&!ctx.classDeclaration().isEmpty()){
//                System.out.println("classDeclarataion");
//                return  visitClassDeclaration(ctx.classDeclaration());
//            }else if (ctx.expressionStatement()!=null&&!ctx.expressionStatement().isEmpty()){
//                System.out.println("expression");
//                return  visitExpressionStatement(ctx.expressionStatement());
//            }else if (ctx.ifStatement()!=null&&!ctx.ifStatement().isEmpty()){
//                System.out.println("if");
//                return  visitIfStatement(ctx.ifStatement());
//            }else if (ctx.iterationStatement()!=null&&!ctx.iterationStatement().isEmpty()){
//                return  null;
//            }else if (ctx.continueStatement()!=null&&!ctx.continueStatement().isEmpty()){
//                System.out.println("continue");
//                return  visitContinueStatement(ctx.continueStatement());
//            }else if (ctx.breakStatement()!=null&&!ctx.breakStatement().isEmpty()){
//                System.out.println("break;");
//                return  visitBreakStatement(ctx.breakStatement());
//            }else if (ctx.returnStatement()!=null&&!ctx.returnStatement().isEmpty()){
//                System.out.println("return");
//                return  visitReturnStatement(ctx.returnStatement());
//            }else if (ctx.yieldStatement()!=null&&!ctx.yieldStatement().isEmpty()){
//                System.out.println("yield");
//                return  visitYieldStatement(ctx.yieldStatement());
//            }else if (ctx.withStatement()!=null&&!ctx.withStatement().isEmpty()){
//                System.out.println("with");
//                return  visitWithStatement(ctx.withStatement());
//            }else if (ctx.labelledStatement()!=null&&!ctx.labelledStatement().isEmpty()){
//                System.out.println("label");
//                return  visitLabelledStatement(ctx.labelledStatement());
//            }else if (ctx.switchStatement()!=null&&!ctx.switchStatement().isEmpty()){
//                System.out.println("switch");
//                return  visitSwitchStatement(ctx.switchStatement());
//            }else if (ctx.throwStatement()!=null&&!ctx.throwStatement().isEmpty()){
//                System.out.println("throw");
//                return  visitThrowStatement(ctx.throwStatement());
//            }else if (ctx.tryStatement()!=null&&!ctx.tryStatement().isEmpty()){
//                System.out.println("try");
//                return  visitTryStatement(ctx.tryStatement());
//            }else if (ctx.debuggerStatement()!=null&&!ctx.debuggerStatement().isEmpty()){
//                System.out.println("debugg");
//                return  visitDebuggerStatement(ctx.debuggerStatement());
//            }else{
//                System.out.println("function");
//                return visitFunctionDeclaration(ctx.functionDeclaration());
//            }
//        }

        @Override
        public Void visitExpressionStatement(JavaScriptParser.ExpressionStatementContext ctx) {
            //    : {this.notOpenBraceAndNotFunction()}? expressionSequence eos
            // 创建表达式结点
            CFNode expr = new CFNode();

            // 设置代码行号和代码内容
            expr.setLineOfCode(ctx.getStart().getLine());
            expr.setCode(getOriginalCodeText(ctx));

            Logger.debug(expr.getLineOfCode() + ":" + expr.getCode());

            // 增加上下文属性
            addContextualProperty(expr, ctx);

            // 将expr node加入cfg，并与preNodes建立preEdges
            addNodeAndPreEdge(expr);

            // 将expr node及普通边加入pre，供后续结点连接使用
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(expr);

            return null;
        }


        @Override
        public Void visitIfStatement(JavaScriptParser.IfStatementContext ctx) {
            //    : If '(' expressionSequence ')' statement (Else statement)?

            // 生成if结点
            CFNode ifNode = new CFNode();

            // 设置相应代码
            ifNode.setLineOfCode(ctx.getStart().getLine());
            ifNode.setCode("if " + getOriginalCodeText(ctx.expressionSequence()));

            // 设置结点属性
            addContextualProperty(ifNode, ctx);

            // 将pre中的结点pop出并与if结点建边
            addNodeAndPreEdge(ifNode);

            // 丢进pre里面 保存上一个结点和边的属性 用于等下连接下一个结点
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(ifNode);

            // 访问子节点
            // 访问过程中会将子节点丢进pre里面
            // 同时会完成将pre中的if结点与子节点建立边的操作
            visit(ctx.statement(0));

            // 生成endif结点
            CFNode endif = new CFNode();

            // 设置相应代码
            endif.setLineOfCode(0);
            endif.setCode("endif");

            // 设置结点属性（此处为空）

            // 将该节点增加到cfg中并用pre中的结点与之建边
            addNodeAndPreEdge(endif);

            // 如果没有else，if为false时直接endif
            if (ctx.statement().size() == 1) { // if without else
                cfg.addEdge(new Edge<>(ifNode, new CFEdge(CFEdge.Type.FALSE), endif));
            } else {  //  if with else
                // 如果有else

                // 设置pre的内容为if结点和类型为false的边
                preEdges.push(CFEdge.Type.FALSE);
                preNodes.push(ifNode);

                // 访问else的代码
                // 其中的结点也会压到pre中
                visit(ctx.statement(1));

                // 将pre中的结点即else的代码pop并与endif结点建边
                popAddPreEdgeTo(endif);
            }

            // 为后面的访问设置好pre
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(endif);
            return null;
        }

        @Override
        public Void visitDoStatement(JavaScriptParser.DoStatementContext ctx) {
            //Do statement While '(' expressionSequence ')' eos
            // 创建do node
            CFNode doNode = new CFNode();

            // 设置do node代码行号和代码内容
            doNode.setLineOfCode(ctx.getStart().getLine());
            doNode.setCode("do");

            // 将do node加入cfg并与preNodes连接
            addNodeAndPreEdge(doNode);

            // 创建while node
            CFNode whileNode = new CFNode();

            // 设置while node代码行号和代码内容
            whileNode.setLineOfCode(ctx.expressionSequence().getStart().getLine());
            whileNode.setCode("while " + getOriginalCodeText(ctx.expressionSequence()));

            // 增加上下文属性
            addContextualProperty(whileNode, ctx);

            // 加入cfg
            cfg.addVertex(whileNode);

            // 创建end node
            CFNode doWhileEnd = new CFNode();

            // 设置end node代码行号和代码内容
            doWhileEnd.setLineOfCode(0);
            doWhileEnd.setCode("end-do-while");

            // 加入cfg
            cfg.addVertex(doWhileEnd);

            // 加入pre
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(doNode);

            // 维护当前循环结构的开始和结束
            loopBlocks.push(new Block(whileNode, doWhileEnd));

            // 访问循环体
            visit(ctx.statement());

            // 弹出当前循环结构
            loopBlocks.pop();

            // 此时preNodes栈顶元素为循环体最后一个结点
            // 让它指向while node
            popAddPreEdgeTo(whileNode);

            // 将while node指向do node，形成循环
            cfg.addEdge(new Edge<>(whileNode, new CFEdge(CFEdge.Type.TRUE), doNode));
            // 将while node指向end node，结束循环
            cfg.addEdge(new Edge<>(whileNode, new CFEdge(CFEdge.Type.FALSE), doWhileEnd));

            // 将end node加入preNodes，供后续连接
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(doWhileEnd);

            return null;
        }

        @Override
        public Void visitWhileStatement(JavaScriptParser.WhileStatementContext ctx) {
            //While '(' expressionSequence ')' statement
            // 创建while语句根节点
            CFNode whileNode = new CFNode();

            // 设置while node代码行号和代码内容
            whileNode.setLineOfCode(ctx.getStart().getLine());
            whileNode.setCode("while " + getOriginalCodeText(ctx.expressionSequence()));

            // 新增while node上下文信息
            addContextualProperty(whileNode, ctx);

            // 将whileNode与cfg上面的结点连接
            addNodeAndPreEdge(whileNode);

            // 创建endwhile node
            CFNode endwhile = new CFNode();

            // 设置代码行号和代码内容
            endwhile.setLineOfCode(0);
            endwhile.setCode("endwhile");

            // 加入cfg，同时与while node建边
            cfg.addVertex(endwhile);
            cfg.addEdge(new Edge<>(whileNode, new CFEdge(CFEdge.Type.FALSE), endwhile));

            // 将while node加入pre，准备处理while loop body
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(whileNode);

            // 将整个循环结构加入loopBlocks，避免处理循环嵌套时可能出现的匹配错误
            loopBlocks.push(new Block(whileNode, endwhile));

            // 访问循环体的内容
            visit(ctx.statement());

            // 弹出栈顶循环体
            loopBlocks.pop();

            // 访问完循环体后，循环体的最后一个结点存在preNodes中
            // 为了循环，需要将这一结点指向while node
            // 因此需要将preNodes栈顶元素弹出指向while node
            popAddPreEdgeTo(whileNode);

            // 将endwhile node加入pre，供后续结点的连接
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(endwhile);

            return null;
        }

        @Override
        public Void visitForStatement(JavaScriptParser.ForStatementContext ctx) {
            //For '(' (expressionSequence | variableDeclarationList)? ';'  expressionSequence? ';' expressionSequence? ')' statement

            // forInit处理
            CFNode forInit;
            int forIndex;
            // for 的init语句有两种可能：表达式序列 或 声明列表
            // 主要区别：代码行号和代码的设置以及上下文属性
            if (ctx.variableDeclarationList() != null) {
                // 若为声明列表
                forIndex = 0;
                forInit = new CFNode();
                forInit.setLineOfCode(ctx.variableDeclarationList().getStart().getLine());
                forInit.setCode(getOriginalCodeText(ctx.variableDeclarationList()));
                addContextualProperty(forInit, ctx.variableDeclarationList());
            } else {
                // 若为表达式序列
                forIndex = 1;
                forInit = new CFNode();
                forInit.setLineOfCode(ctx.expressionSequence(0).getStart().getLine());
                forInit.setCode(getOriginalCodeText(ctx.expressionSequence(0)));
                addContextualProperty(forInit, ctx.expressionSequence(0));
            }
            // 设置完属性后与先前CFG连接
            addNodeAndPreEdge(forInit);

            // for-expression处理
            CFNode forExpr = new CFNode();
            // 判断是否有循环条件
            if (ctx.expressionSequence(forIndex) == null) {
                forExpr.setLineOfCode(ctx.expressionSequence(forIndex).getStart().getLine());
                forExpr.setCode("for ( ; )");
            } else {
                forExpr.setLineOfCode(ctx.expressionSequence(forIndex).getStart().getLine());
                forExpr.setCode("for (" + getOriginalCodeText(ctx.expressionSequence(forIndex)) + ")");
            }
            addContextualProperty(forExpr, ctx.expressionSequence(forIndex));

            // 新增forExpr node并建立forInit到forExpr的边
            cfg.addVertex(forExpr);
            cfg.addEdge(new Edge<>(forInit, new CFEdge(CFEdge.Type.EPSILON), forExpr));

            // for-update处理
            CFNode forUpdate = new CFNode();
            // 判断每次循环过后是否有更新
            if (ctx.expressionSequence(forIndex + 1) == null) { // empty for-update
                forUpdate.setCode(" ; ");
                forUpdate.setLineOfCode(ctx.expressionSequence(forIndex + 1).getStart().getLine());
            } else {
                forUpdate.setCode(getOriginalCodeText(ctx.expressionSequence(forIndex + 1)));
                forUpdate.setLineOfCode(ctx.expressionSequence(forIndex + 1).getStart().getLine());
            }
            // 设置上下文属性并加入cfg
            addContextualProperty(forUpdate, ctx.expressionSequence(forIndex + 1));
            cfg.addVertex(forUpdate);

            // 创建forEnd node
            CFNode forEnd = new CFNode();
            forEnd.setLineOfCode(0);
            forEnd.setCode("endfor");
            cfg.addVertex(forEnd);
            // 若forExpr为false直接到forEnd
            cfg.addEdge(new Edge<>(forExpr, new CFEdge(CFEdge.Type.FALSE), forEnd));
            // 压入pre中准备解析循环体
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(forExpr);

            // 记录循环体解析嵌套关系
            loopBlocks.push(new Block(forUpdate, forEnd)); // NOTE: start is 'forUpdate'
            visit(ctx.statement());
            loopBlocks.pop();

            // 循环体解析完成后指向forUpdate，forUpdate再指向forExpr
            popAddPreEdgeTo(forUpdate);
            cfg.addEdge(new Edge<>(forUpdate, new CFEdge(CFEdge.Type.EPSILON), forExpr));

            // 将forEnd加入pre，结束for分析
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(forEnd);

            return null;
        }

        /**
         * for..in 循环（用于对象的属性迭代）
         * @param ctx 相关上下文
         * @return Void
         */
        @Override
        public Void visitForInStatement(JavaScriptParser.ForInStatementContext ctx) {
            // 错误文法：只能声明一个变量
            // For '(' (singleExpression | variableDeclarationList) In expressionSequence ')' statement
            /**
             * for ( [lookahead ≠ let [] LeftHandSideExpression[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
             * for ( var ForBinding[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
             * for ( ForDeclaration[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
             */

            // forExpr处理
            CFNode forExpr = new CFNode();
            // 判断是单个表达式还是变量声明序列
            if (ctx.singleExpression() != null) {
                // for ( var ForBinding in Expression) Statement
                // 使用一个已声明的变量ForBinding，用于在循环中迭代对象的属性名，Expression 是要遍历的对象。
                forExpr.setLineOfCode(ctx.statement().getStart().getLine());
                forExpr.setCode("for(" + getOriginalCodeText(ctx.singleExpression()) + " in " + getOriginalCodeText(ctx.expressionSequence()) + ")");
                addContextualProperty(forExpr, ctx.singleExpression());
            } else {
                // for ( ForDeclaration in Expression) Statement
                // 这种形式使用 var、let、const 声明了一个变量，用于在循环中迭代对象的属性名，Expression 是要遍历的对象。
                forExpr.setLineOfCode(ctx.variableDeclarationList().getStart().getLine());

                // 声明多个变量时为语法错误
                if (ctx.variableDeclarationList().variableDeclaration().size() > 1)
                    forExpr.setCode("Syntax Error: for(" + getOriginalCodeText(ctx.variableDeclarationList()) + " in " + getOriginalCodeText(ctx.expressionSequence()) + ")");
                else
                    forExpr.setCode("for(" + getOriginalCodeText(ctx.variableDeclarationList()) + " in " + getOriginalCodeText(ctx.expressionSequence()) + ")");

                addContextualProperty(forExpr, ctx.variableDeclarationList());

                // 打印单个或所有声明的变量
//                System.out.println(ctx.variableDeclarationList().variableDeclaration(0).getText());
//                for (JavaScriptParser.VariableDeclarationContext context : ctx.variableDeclarationList().variableDeclaration()) {
//                    System.out.println(context.getText());
//                }
            }
            addNodeAndPreEdge(forExpr);

            // 创建forEnd node
            CFNode forEnd = new CFNode();
            forEnd.setLineOfCode(0);
            forEnd.setCode("endfor");
            // 处理语句控制关系：forExpr为FALSE时结束for循环
            cfg.addVertex(forEnd);
            cfg.addEdge(new Edge<>(forExpr, new CFEdge(CFEdge.Type.FALSE), forEnd));

            // 加入pre中供循环体连接
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(forExpr);

            // 记录当前处理循环体
            loopBlocks.push(new Block(forExpr, forEnd));
            visit(ctx.statement());
            loopBlocks.pop();

            // 将子节点与forExpr连接
            popAddPreEdgeTo(forExpr);

            // 将forEnd加入pre供后续结点连接，结束循环
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(forEnd);

            return null;
        }

        /**
         * for..of 循环（用于迭代可迭代对象的值）
         * @param ctx 相关上下文
         * @return Void
         */
        @Override
        public Void visitForOfStatement(JavaScriptParser.ForOfStatementContext ctx) {
            // 错误文法：没有identifier也不能声明多个变量
            // identifier{this.p("of")}即为of
            //    | For Await? '(' (singleExpression | variableDeclarationList) identifier{this.p("of")}? expressionSequence ')' statement  # ForOfStatement
            /**
             * for ( [lookahead ∉ { let, async of }] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
             * for ( var ForBinding[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
             * for ( ForDeclaration[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
             */

            CFNode forExpr = new CFNode();
            String await = ctx.Await() != null ? "await" : "";
            if (ctx.singleExpression() != null) {
                forExpr.setLineOfCode(ctx.statement().getStart().getLine());
                System.out.println(ctx.identifier());
                forExpr.setCode("for " + await + " (" + getOriginalCodeText(ctx.singleExpression()) + " of " + getOriginalCodeText(ctx.expressionSequence()) + ")");
                addContextualProperty(forExpr, ctx.singleExpression());
            } else {
                forExpr.setLineOfCode(ctx.variableDeclarationList().getStart().getLine());
                if (ctx.variableDeclarationList().variableDeclaration().size() > 1)
                    forExpr.setCode("Syntax Error: for " + await + " (" + getOriginalCodeText(ctx.variableDeclarationList()) + " of " + getOriginalCodeText(ctx.expressionSequence()) + ")");
                else
                    forExpr.setCode("for " + await + " (" + getOriginalCodeText(ctx.variableDeclarationList()) + " of " + getOriginalCodeText(ctx.expressionSequence()) + ")");
                addContextualProperty(forExpr, ctx.variableDeclarationList());
            }

            addNodeAndPreEdge(forExpr);
            //
            CFNode forEnd = new CFNode();
            forEnd.setLineOfCode(0);
            forEnd.setCode("endfor");
            cfg.addVertex(forEnd);
            cfg.addEdge(new Edge<>(forExpr, new CFEdge(CFEdge.Type.FALSE), forEnd));
            //
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(forExpr);
            //
            loopBlocks.push(new Block(forExpr, forEnd));
            visit(ctx.statement());
            loopBlocks.pop();
            popAddPreEdgeTo(forExpr);
            //
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(forEnd);

            return null;
        }

        /**
         * 处理var, let, const单个声明语句，也能处理变量声明列表
         * @param ctx
         * @return
         */
        @Override
        public Void visitVariableStatement(JavaScriptParser.VariableStatementContext ctx) {
            CFNode varNode = new CFNode();
            varNode.setLineOfCode(ctx.getStart().getLine());
            varNode.setCode(getOriginalCodeText(ctx));
            addContextualProperty(varNode, ctx);
            addNodeAndPreEdge(varNode);
//            System.out.println(ctx.getStart().getText());

            // 声明语句也需要连接
//            dontPop = true;
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(varNode);

            return null;
        }


//        /**
//         * 处理声明
//         */
//        @Override
//        public Void visitDeclaration(JavaScriptParser.DeclarationContext ctx) {
//            CFNode varNode = new CFNode();
//            varNode.setLineOfCode(ctx.getStart().getLine());
//            varNode.setCode(getOriginalCodeText(ctx));
//            addContextualProperty(varNode, ctx);
//            addNodeAndPreEdge(varNode);
//            System.out.println(ctx.getStart().getText());
//
//            // 声明语句也需要连接
////            dontPop = true;
//            preEdges.push(CFEdge.Type.EPSILON);
//            preNodes.push(varNode);
//
//            return null;
//        }

//        /**
//         * 处理Var修饰符
//         */
//        @Override
//        public Void visitVarModifier(JavaScriptParser.VarModifierContext ctx) {
//            CFNode varNode = new CFNode();
//            varNode.setLineOfCode(ctx.getStart().getLine());
//            varNode.setCode(getOriginalCodeText(ctx));
//            addContextualProperty(varNode, ctx);
//            addNodeAndPreEdge(varNode);
//
//            // 声明语句不需要连接
//            dontPop = true;
//            return null;
//        }

//        /**
//         * 处理Var, let, const修饰符后的变量声明
//         */
//        @Override
//        public Void visitVariableDeclaration(JavaScriptParser.VariableDeclarationContext ctx) {
//            CFNode varNode = new CFNode();
//            varNode.setLineOfCode(ctx.getStart().getLine());
//            varNode.setCode("var " + getOriginalCodeText(ctx));
//            addContextualProperty(varNode, ctx);
//            addNodeAndPreEdge(varNode);
//            System.out.println(ctx.getStart().getText());
//
//            // 声明语句也需要连接
////            dontPop = true;
//            preEdges.push(CFEdge.Type.EPSILON);
//            preNodes.push(varNode);
//
//            return null;
//        }

        /**
         * 处理continue，与上文连接即可不用与下文再连接（与下文不具备控制关系）
         * @param ctx
         * @return
         */
        @Override
        public Void visitContinueStatement(JavaScriptParser.ContinueStatementContext ctx) {
            //    : Continue ({this.notLineTerminator()}? identifier)? eos

            // 创建continue node
            CFNode continueNode = new CFNode();
            // 设置代码行号、代码内容和上下文信息
            continueNode.setLineOfCode(ctx.getStart().getLine());
            continueNode.setCode(getOriginalCodeText(ctx));
            addContextualProperty(continueNode, ctx);
            // 与上文结点进行连接
            addNodeAndPreEdge(continueNode);

            // 判断是否有指定跳转到哪个label
            if (ctx.identifier() != null) {
                // a label is specified
                for (Block block : labeledBlocks) {
                    // 遍历维护的循环栈，找到正确跳转到的循环的开始
                    if (block.label.equals(ctx.identifier().Identifier().getText())) {
                        cfg.addEdge(new Edge<>(continueNode, new CFEdge(CFEdge.Type.EPSILON), block.start));
                        break;
                    }
                }
            } else {
                // no label
                // 直接跳转到当前循环的开始即可
                Block block = loopBlocks.peek();
                cfg.addEdge(new Edge<>(continueNode, new CFEdge(CFEdge.Type.EPSILON), block.start));
            }
            dontPop = true;
            return null;
        }

        /**
         * 处理break，与上文连接即可不用与下文再连接（与下文不具备控制关系）
         * @param ctx
         * @return
         */
        @Override
        public Void visitBreakStatement(JavaScriptParser.BreakStatementContext ctx) {
            //    : Break ({this.notLineTerminator()}? identifier)? eos
            CFNode breakNode = new CFNode();
            breakNode.setLineOfCode(ctx.getStart().getLine());
            breakNode.setCode(getOriginalCodeText(ctx));
            addContextualProperty(breakNode, ctx);
            addNodeAndPreEdge(breakNode);
            if (ctx.identifier() != null) {
                // a label is specified
                for (Block block : labeledBlocks) {
                    if (block.label.equals(ctx.identifier().Identifier().getText())) {
                        cfg.addEdge(new Edge<>(breakNode, new CFEdge(CFEdge.Type.EPSILON), block.end));
                        break;
                    }
                }
            } else {
                // no label
                // 注意不能将block的end除去，因为可能不止一个break需要指向当前代码块的结尾（如break）
                Block block = loopBlocks.peek();
                cfg.addEdge(new Edge<>(breakNode, new CFEdge(CFEdge.Type.EPSILON), block.end));
            }
            dontPop = true;
            return null;
        }

        /**
         * 处理return，与上文连接即可不用与下文再连接（与下文不具备控制关系）
         * @param ctx
         * @return
         */
        @Override
        public Void visitReturnStatement(JavaScriptParser.ReturnStatementContext ctx) {
            //    : Return ({this.notLineTerminator()}? expressionSequence)? eos
            CFNode ret = new CFNode();
            ret.setLineOfCode(ctx.getStart().getLine());
            ret.setCode(getOriginalCodeText(ctx));
            addContextualProperty(ret, ctx);
            addNodeAndPreEdge(ret);
            dontPop = true;
            return null;
        }

        /**
         *
         * @param ctx
         * @return
         */
        @Override
        public Void visitYieldStatement(JavaScriptParser.YieldStatementContext ctx) {
            CFNode yieldNode = new CFNode();
            yieldNode.setLineOfCode(ctx.getStart().getLine());
            yieldNode.setCode(getOriginalCodeText(ctx));
            addContextualProperty(yieldNode, ctx);
            addNodeAndPreEdge(yieldNode);
            dontPop = true;
            return null;
        }

        /**
         * 处理with
         * @param ctx
         * @return
         */
        @Override
        public Void visitWithStatement(JavaScriptParser.WithStatementContext ctx) {
            //    : With '(' expressionSequence ')' statement
            CFNode withNode = new CFNode();
            withNode.setLineOfCode(ctx.getStart().getLine());
            withNode.setCode("with (" + getOriginalCodeText(ctx.expressionSequence()) + ")");
            addContextualProperty(withNode, ctx);
            addNodeAndPreEdge(withNode);
            //
            CFNode endWith = new CFNode();
            endWith.setLineOfCode(0);
            endWith.setCode("end-with");
            cfg.addVertex(endWith);
            //
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(withNode);
            loopBlocks.push(new Block(withNode, endWith));
            //
            visit(ctx.statement());
            loopBlocks.pop();
            popAddPreEdgeTo(endWith);
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(endWith);
            return null;
        }

        /**
         * 处理switch语句
         * @param ctx
         * @return
         * todo 记录一下思路和细节
         */
        @Override
        public Void visitSwitchStatement(JavaScriptParser.SwitchStatementContext ctx) {
            /**
             * SwitchStatement[Yield, Await, Return] :
             *      switch ( Expression[+In, ?Yield, ?Await] ) CaseBlock[?Yield, ?Await, ?Return]
             *
             * CaseBlock[Yield, Await, Return] :
             *      { CaseClauses[?Yield, ?Await, ?Return]opt }
             *      { CaseClauses[?Yield, ?Await, ?Return]opt DefaultClause[?Yield, ?Await, ?Return] CaseClauses[?Yield, ?Await, ?Return]opt }
             *
             * CaseClauses[Yield, Await, Return] :
             *      CaseClause[?Yield, ?Await, ?Return]
             *      CaseClauses[?Yield, ?Await, ?Return] CaseClause[?Yield, ?Await, ?Return]
             *
             * CaseClause[Yield, Await, Return] :
             *      case Expression[+In, ?Yield, ?Await] : StatementList[?Yield, ?Await, ?Return]opt
             *
             * DefaultClause[Yield, Await, Return] :
             *      default : StatementList[?Yield, ?Await, ?Return]opt
             *
             * break不能带有label
             */

            //    : Switch '(' expressionSequence ')' caseBlock
            // 创建switch node并进行相关处理
            CFNode switchNode = new CFNode();
            switchNode.setLineOfCode(ctx.getStart().getLine());
            switchNode.setCode("switch " + getOriginalCodeText(ctx.expressionSequence()));
            addContextualProperty(switchNode, ctx);
            addNodeAndPreEdge(switchNode);

            // 创建endswitch node并进行相关处理
            CFNode endSwitch = new CFNode();
            endSwitch.setLineOfCode(0);
            endSwitch.setCode("end-switch");
            // 先将endSwitch加入cfg，因为后续要限定switch的范围
            cfg.addVertex(endSwitch);

            // 开始处理各自case
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(switchNode);

            // 限定范围
            loopBlocks.push(new Block(switchNode, endSwitch));
            // 初始没有preCase
            CFNode preCase = null;

            // caseBolck下有若干个caseClauses，遍历每个caseClauses（实际上只有一个）
//            for (JavaScriptParser.CaseClausesContext caseClauses : ctx.caseBlock().caseClauses()) {
//                //    : '{' caseClauses? (defaultClause caseClauses?)? '}'
//                // 处理每个case，并将当前case记为pre，供后续使用
//                System.out.println("current caseClauses: " + caseClauses.getText());
//                preCase = visitSwitchCaseClauses(caseClauses, preCase);
////                for (JavaScriptParser.CaseClauseContext caseClause : caseClauses.caseClause()) {
//////                    System.out.println("\tcurrent caseClause: " + caseClause.getText());
////                    visit(caseClause);
////                }
////                System.out.println("----------------------");
//            }

            // 访问每个case和default
            for (JavaScriptParser.CaseClausesContext caseClauses : ctx.caseBlock().caseClauses()) {
                for (JavaScriptParser.CaseClauseContext caseClause : caseClauses.caseClause()) {
                    preCase = visitSwitchCaseClause(caseClause, preCase);
                }
            }
            preCase = visitSwitchCaseClause(ctx.caseBlock().defaultClause(), preCase);

            loopBlocks.pop();
            popAddPreEdgeTo(endSwitch);
            // 将最后一个case与endSwitch相连，但default就不用
            if (preCase != null && !preCase.getCode().equals("default:"))
                cfg.addEdge(new Edge<>(preCase, new CFEdge(CFEdge.Type.FALSE), endSwitch));

            // 加入pre供后续处理
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(endSwitch);

            return null;
        }

        private CFNode visitSwitchCaseClause(ParserRuleContext ctx, CFNode preCase) {
            if (ctx == null)
                return null;

            // 创建这个case的结点
            CFNode caseStmt = new CFNode();
            caseStmt.setLineOfCode(ctx.getStart().getLine());
            // 判断是default还是普通case
            int sequenceStartIndex = 0;
            if (ctx.getChild(0).getText().equals("default")) {
                caseStmt.setCode("default:");
                sequenceStartIndex = 2;
            } else {
                caseStmt.setCode(ctx.getChild(0).getText() + " " + ctx.getChild(1).getText() + ":");
                sequenceStartIndex = 3;
            }

            if (preCase == null) {
                // 第一个case，与pre中的switch连接
                addNodeAndPreEdge(caseStmt);
            } else {
                // 非第一个case，加入cfg并与先前case连接
                cfg.addVertex(caseStmt);
                cfg.addEdge(new Edge<>(preCase, new CFEdge(CFEdge.Type.FALSE), caseStmt));
            }

            // 加入casesQueue供expressionSequence的第一个结点连接
            if (ctx.getChild(sequenceStartIndex) != null) {
                casesQueue.add(caseStmt);
            }
            visit(ctx);

            return caseStmt;
        }

        // 旧实现
//        private CFNode visitSwitchCaseClauses(JavaScriptParser.CaseClausesContext caseClauses, CFNode preCase) {
//            //    : Case expressionSequence ':' statementList?
//            CFNode caseStmt = preCase;
//            // 遍历每个case，包括case及case的内容
//            for (JavaScriptParser.CaseClauseContext ctx : caseClauses.caseClause()) {
//                // 创建这个case的结点
//                caseStmt = new CFNode();
//                caseStmt.setLineOfCode(ctx.getStart().getLine());
//                caseStmt.setCode(ctx.getChild(0).getText() + " " + ctx.getChild(1).getText() + ":");
//
//                if (preCase == null) {
//                    // 第一个case，与pre中的switch连接
//                    addNodeAndPreEdge(caseStmt);
//                } else {
//                    // 非第一个case，加入cfg并与先前case连接
//                    cfg.addVertex(caseStmt);
//                    cfg.addEdge(new Edge<>(preCase, new CFEdge(CFEdge.Type.FALSE), caseStmt));
//                }
//                preCase = caseStmt;
//
//                // 加入casesQueue供expressionSequence的第一个结点连接
//                System.out.println();
//                if (ctx.getChild(3) != null)
//                    casesQueue.add(caseStmt);
//                // 访问每个caseStmt下的语句
////                visit(ctx.expressionSequence());
//                visit(ctx);
//
//                // 第一个case，与switch node相连
////                if (preCase == null)
////                    addNodeAndPreEdge(caseStmt);
////                cfg.addVertex(caseStmt);
////                if (dontPop)
////                    dontPop = false;
////                else
////                    cfg.addEdge(new Edge<>(preNodes.pop(), new CFEdge(preEdges.pop()), caseStmt));
//
//                // 将当前case加入caseQueues
////                casesQueue.add(caseStmt);
//
////                if (preCase != null)
////                    cfg.addEdge(new Edge<>(preCase, new CFEdge(CFEdge.Type.FALSE), caseStmt));
//////                else
//////                    cfg.addEdge(new Edge<>())
////                if (ctx.getStart().getText().equals("default")) {
////                    preEdges.push(CFEdge.Type.EPSILON);
////                    preNodes.push(caseStmt);
////                    caseStmt = null;
////                } else { // any other case ...
////                    dontPop = true;
////                    casesQueue.add(caseStmt);
////                    preCase = caseStmt;
////                }
//            }
//
//            return caseStmt;
//        }

        /**
         *
         * @param ctx
         * @return
         */
        @Override
        public Void visitLabelledStatement(JavaScriptParser.LabelledStatementContext ctx) {
            //labelledStatement
            //    : identifier ':' statement

            // 创建label node
            CFNode labelNode = new CFNode();
            labelNode.setLineOfCode(ctx.getStart().getLine());
            labelNode.setCode(ctx.identifier().Identifier() + ": ");
            // 增加上下文属性
            addContextualProperty(labelNode, ctx);
            // 与cfg连接
            addNodeAndPreEdge(labelNode);

            // 创建endLabel node
            CFNode endLabelNode = new CFNode();
            endLabelNode.setLineOfCode(0);
            endLabelNode.setCode("end-label");
            // 加入cfg
            cfg.addVertex(endLabelNode);
            // 加入pre，用于后续stmt node的连接
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(labelNode);

            // 限定label作用的语句
            Block currentBlock = new Block(labelNode, endLabelNode, ctx.identifier().Identifier().getText());
            labeledBlocks.add(currentBlock);
            // 访问label后的语句
            visit(ctx.statement());
            // 弹出label作用域，防止label复用时造产生错误
            labeledBlocks.remove(currentBlock);

            // 将endLabel node与label内最后一个结点连接
            popAddPreEdgeTo(endLabelNode);
            // 加入pre，结束对label的处理
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(endLabelNode);

            return null;
        }

        /**
         * 处理throw
         * @param ctx
         * @return
         */
        @Override
        public Void visitThrowStatement(JavaScriptParser.ThrowStatementContext ctx) {
            CFNode throwNode = new CFNode();
            throwNode.setLineOfCode(ctx.getStart().getLine());
            throwNode.setCode(getOriginalCodeText(ctx));
            addContextualProperty(throwNode, ctx);
            // 与先前cfg连接
            addNodeAndPreEdge(throwNode);

            if (tryBlocks.size() != 0) {
                // throw在tryBlock中，与end try连接
                cfg.addEdge(new Edge<>(throwNode, new CFEdge(CFEdge.Type.EPSILON), tryBlocks.peekLast().end));
            } else {
                // throw不在tryBlock中，不用弹出
                dontPop = true;
            }

            return null;
        }

        /**
         * 处理try
         * @param ctx
         * @return
         */
        @Override
        public Void visitTryStatement(JavaScriptParser.TryStatementContext ctx) {
            //    : Try block (catchProduction finallyProduction? | finallyProduction)
            CFNode tryNode = new CFNode();
            tryNode.setLineOfCode(ctx.getStart().getLine());
            tryNode.setCode("try");
            addContextualProperty(tryNode, ctx);
            // 与先前结点连接
            addNodeAndPreEdge(tryNode);

            // 创建endTry node
            CFNode endTry = new CFNode();
            endTry.setLineOfCode(0);
            endTry.setCode("end-try");
            cfg.addVertex(endTry);
            //
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(tryNode);

            // 限定try范围
            tryBlocks.push(new Block(tryNode, endTry));
            visit(ctx.block());
            popAddPreEdgeTo(endTry);

            // If there is a finally-block, visit it first
            CFNode finallyNode = null;
            CFNode endFinally = null;
            if (ctx.finallyProduction() != null) {
                // 'finally' block
                finallyNode = new CFNode();
                finallyNode.setLineOfCode(ctx.finallyProduction().getStart().getLine());
                finallyNode.setCode("finally");
                addContextualProperty(finallyNode, ctx.finallyProduction());
                cfg.addVertex(finallyNode);
                cfg.addEdge(new Edge<>(endTry, new CFEdge(CFEdge.Type.EPSILON), finallyNode));
                //
                preEdges.push(CFEdge.Type.EPSILON);
                preNodes.push(finallyNode);
                visit(ctx.finallyProduction().block());
                //
                endFinally = new CFNode();
                endFinally.setLineOfCode(0);
                endFinally.setCode("end-finally");
                addNodeAndPreEdge(endFinally);
            }
            // Now visit any available catch clauses
            if (ctx.catchProduction() != null) {
                // 'catch' '(' variableModifier* catchType Identifier ')' block
                CFNode catchNode;
                CFNode endCatch = new CFNode();
                endCatch.setLineOfCode(0);
                endCatch.setCode("end-catch");
                cfg.addVertex(endCatch);
                catchNode = new CFNode();
                catchNode.setLineOfCode(ctx.catchProduction().getStart().getLine());
                String catchSourceText = ctx.catchProduction().getText();
                catchNode.setCode(catchSourceText.substring(0, catchSourceText.indexOf(')') + 1));
                addContextualProperty(catchNode, ctx.catchProduction());
                cfg.addVertex(catchNode);
                cfg.addEdge(new Edge<>(endTry, new CFEdge(CFEdge.Type.THROWS), catchNode));
                preEdges.push(CFEdge.Type.EPSILON);
                preNodes.push(catchNode);
                visit(ctx.catchProduction().block());
                popAddPreEdgeTo(endCatch);
                if (finallyNode != null) {
                    // connect end-catch node to finally-node,
                    // and push end-finally to the stack ...
                    cfg.addEdge(new Edge<>(endCatch, new CFEdge(CFEdge.Type.EPSILON), finallyNode));
                    preEdges.push(CFEdge.Type.EPSILON);
                    preNodes.push(endFinally);
                } else {
                    // connect end-catch node to end-try,
                    // and push end-try to the the stack ...
                    cfg.addEdge(new Edge<>(endCatch, new CFEdge(CFEdge.Type.EPSILON), endTry));
                    preEdges.push(CFEdge.Type.EPSILON);
                    preNodes.push(endTry);
                }
            } else {
                // No catch-clause; it's a try-finally
                // push end-finally to the stack ...
                preEdges.push(CFEdge.Type.EPSILON);
                preNodes.push(endFinally);
            }

            tryBlocks.pop();
            return null;
        }

        /**
         * try中已经处理，不会调用到
         * @param ctx
         * @return
         */
        @Override
        public Void visitCatchProduction(JavaScriptParser.CatchProductionContext ctx) {
            CFNode catchNode = new CFNode();
            catchNode.setLineOfCode(ctx.getStart().getLine());
            catchNode.setCode("catch(" + getOriginalCodeText(ctx.assignable()) + ")");
            addContextualProperty(catchNode, ctx);
            addNodeAndPreEdge(catchNode);
            CFNode endCatch = new CFNode();
            endCatch.setLineOfCode(0);
            endCatch.setCode("end-catch");
            cfg.addVertex(endCatch);
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(endCatch);
            labeledBlocks.add(new Block(catchNode, endCatch));
            visit(ctx.block());
            popAddPreEdgeTo(endCatch);
            dontPop = true;
            return null;
        }

        /**
         * try中已经处理，不会调用到
         * @param ctx
         * @return
         */
        @Override
        public Void visitFinallyProduction(JavaScriptParser.FinallyProductionContext ctx) {
            CFNode finalNode = new CFNode();
            finalNode.setLineOfCode(ctx.getStart().getLine());
            finalNode.setCode(getOriginalCodeText(ctx));
            addContextualProperty(finalNode, ctx);
            addNodeAndPreEdge(finalNode);
            CFNode endFinal = new CFNode();
            endFinal.setLineOfCode(0);
            endFinal.setCode("end-finally");
            cfg.addVertex(endFinal);
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(endFinal);
            labeledBlocks.add(new Block(finalNode, endFinal));
            visit(ctx.block());
            popAddPreEdgeTo(endFinal);
            dontPop = true;
            return null;
        }

        /**
         *
         * @param ctx
         * @return
         */
        @Override
        public Void visitDebuggerStatement(JavaScriptParser.DebuggerStatementContext ctx) {
            CFNode ret = new CFNode();
            ret.setLineOfCode(ctx.getStart().getLine());
            ret.setCode(getOriginalCodeText(ctx));
            addContextualProperty(ret, ctx);
            addNodeAndPreEdge(ret);
            // dontPop = true;
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(ret);
            return null;
        }

        /**
         * 测试方法声明
         * @param ctx
         * @return
         * todo Async function直接不走这个函数了
         */
        @Override
        public Void visitFunctionDeclaration(JavaScriptParser.FunctionDeclarationContext ctx) {
            //    : Async? Function_ '*'? identifier '(' formalParameterList? ')' functionBody
            init();
            StringBuilder t = new StringBuilder();
            for (int i = 0; i < ctx.getChildCount(); i++) {
//                System.out.println("token " + i + ": " + ctx.getChild(i).getText() + " " + ctx.getChild(i).getClass());
                if (ctx.getChild(i).getText().equals("*"))
                    t.append("*");
            }
            // 一个函数的入口
            CFNode entry = new CFNode();
            entry.setLineOfCode(ctx.getStart().getLine());

            // 若函数有参数则配置
            String args = "";
            if (ctx.formalParameterList() != null && !ctx.formalParameterList().isEmpty()) {
                args = getOriginalCodeText(ctx.formalParameterList());
            }

            // 函数的入口为 修饰符 + 形参列表
            entry.setCode("function" + t + ": " + ctx.identifier().Identifier() + "(" + args + ")");
            addContextualProperty(entry, ctx);
            cfg.addVertex(entry);

            // 设置entry node的属性和所属类
            entry.setProperty("name", ctx.identifier().Identifier().getText());
            entry.setProperty("class", classNames.peek());
            entry.setProperty("type", "undefined");
            cfg.addMethodEntry(entry);

            // 加入pre
            preNodes.push(entry);
            preEdges.push(CFEdge.Type.EPSILON);
            return visitChildren(ctx);
        }

        /**
         * 处理类的声明
         * @param ctx
         * @return
         */
        @Override
        public Void visitClassDeclaration(JavaScriptParser.ClassDeclarationContext ctx) {
            //Class identifier classTail
            // 用classNames来限定类的范围
            classNames.push(getOriginalCodeText(ctx.identifier()));
            // 直接访问类的内部即可，cfg中不必处理类的声明
            visitChildren(ctx);
            classNames.pop();

            // CFG中不必处理类的声明
//            CFNode classNode = new CFNode();
//            classNode.setCode(getOriginalCodeText(ctx));
//            classNode.setLineOfCode(ctx.getStart().getLine());
//            cfg.addVertex(classNode);
//            cfg.addMethodEntry(classNode);
//            preNodes.push(classNode);
//            preEdges.push(CFEdge.Type.EPSILON);
            return null;
        }

//        /**
//         * 处理类声明的尾部，CFG中不必实现
//         * @param ctx
//         * @return
//         */
//        @Override
//        public Void visitClassTail(JavaScriptParser.ClassTailContext ctx) {
//            //    : (Extends singleExpression)? '{' classElement* '}'
//            // 继承关键字与类成员
//            CFNode classTail = new CFNode();
//            String extend = "";
//            if (ctx.singleExpression() != null) {
//                extend = getOriginalCodeText(ctx.singleExpression());
//            }
//            for (JavaScriptParser.ClassElementContext classElement : ctx.classElement()) {
//                extend += " ";
//                extend += getOriginalCodeText(classElement);
//            }
//            classTail.setCode(extend);
//            classTail.setLineOfCode(ctx.getStart().getLine());
//            cfg.addVertex(classTail);
//            cfg.addMethodEntry(classTail);
//            preNodes.push(classTail);
//            preEdges.push(CFEdge.Type.EPSILON);
//            return visitChildren(ctx);
//        }

        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        @Override
        public Void visitClassElement(JavaScriptParser.ClassElementContext ctx) {
            //classElement
            //    : (Static | {this.n("static")}? identifier | Async)* (methodDefinition | assignable '=' objectLiteral ';')
            //    | emptyStatement_
            //    | '#'? propertyName '=' singleExpression
            //    ;
            return visitChildren(ctx);
        }

        @Override
        public Void visitNewExpression(JavaScriptParser.NewExpressionContext ctx) {
            return super.visitNewExpression(ctx);
        }

        /**
         * 处理类方法
         * @param ctx
         * @return
         */
        @Override
        public Void visitMethodDefinition(JavaScriptParser.MethodDefinitionContext ctx) {
            //    : '*'? '#'? propertyName '(' formalParameterList? ')' functionBody
            //    | '*'? '#'? getter '(' ')' functionBody
            //    | '*'? '#'? setter '(' formalParameterList? ')' functionBody

            // 创建方法头部结点并处理
            CFNode mdNode = new CFNode();
            mdNode.setLineOfCode(ctx.getStart().getLine());
            // 判断方法类型分别处理
            if (ctx.propertyName() != null) {
                String args = ctx.formalParameterList() == null ? "" : getOriginalCodeText(ctx.formalParameterList());
                mdNode.setCode(getOriginalCodeText(ctx.propertyName()) + "(" + args + ")");
                addContextualProperty(mdNode, ctx);
                mdNode.setProperty("name", ctx.propertyName().getText());
            } else if (ctx.getter() != null) {
                mdNode.setCode(getOriginalCodeText(ctx.getter()) + "()");
                addContextualProperty(mdNode, ctx);
                mdNode.setProperty("name", ctx.getter().getText());
            } else {
                mdNode.setCode(getOriginalCodeText(ctx.setter()) + "(" + getOriginalCodeText(ctx.formalParameterList()) + ")");
                addContextualProperty(mdNode, ctx);
                mdNode.setProperty("name", ctx.setter().getText());
            }
            mdNode.setProperty("class", classNames.peek());
            mdNode.setProperty("type", "undefined");
            cfg.addMethodEntry(mdNode);
            // 将结点加入cfg并建边连接
            addNodeAndPreEdge(mdNode);
            // 加入pre
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(mdNode);

            // 创建方法结束结点
            CFNode mdEnd = new CFNode();
            mdEnd.setLineOfCode(0);
            mdEnd.setCode("end-method");

            // 处理方法体
            labeledBlocks.add(new Block(mdNode, mdEnd));
            visit(ctx.functionBody());
            labeledBlocks.remove(labeledBlocks.size() - 1);

            // 将访问完的方法体最后一个结点指向mdEnd
            addNodeAndPreEdge(mdEnd);

            // mdEnd加入pre供后续操作
//            preEdges.push(CFEdge.Type.EPSILON);
//            preNodes.push(mdEnd);

            // 按照java这块的实现不需要与mdEnd建边
            dontPop = true;

            return null;
        }


//        /**
//         * 处理let修饰符
//         * @param ctx
//         * @return
//         */
//        @Override
//        public Void visitLet_(JavaScriptParser.Let_Context ctx) {
//            //    : Let ({this.notLineTerminator()}? expressionSequence)? eos
//
//            CFNode let = new CFNode();
//            let.setLineOfCode(ctx.getStart().getLine());
//            let.setCode(getOriginalCodeText(ctx));
//            addContextualProperty(let, ctx);
//            addNodeAndPreEdge(let);
//            dontPop = true;
//            return null;
//        }

//        /**
//         * 不用单独处理形参列表
//         * @param ctx
//         * @return
//         */
//        @Override
//        public Void visitFormalParameterList(JavaScriptParser.FormalParameterListContext ctx) {
//            if (ctx.formalParameterArg() == null || ctx.formalParameterArg().size() == 0) {
//                return visitLastFormalParameterArg(ctx.lastFormalParameterArg());
//            }
//            CFNode ret = new CFNode();
//            ret.setLineOfCode(ctx.getStart().getLine());
//            StringBuffer stringBuffer = new StringBuffer();
//            stringBuffer.append(getOriginalCodeText(ctx.formalParameterArg(0)));
//            for (int i = 1; i < ctx.formalParameterArg().size(); i++) {
//                stringBuffer.append(",");
//                stringBuffer.append(getOriginalCodeText(ctx.formalParameterArg(i)));
//            }
//            stringBuffer.append(",");
//            if (ctx.lastFormalParameterArg() != null && !ctx.lastFormalParameterArg().isEmpty()) {
//                stringBuffer.append(getOriginalCodeText(ctx.lastFormalParameterArg()));
//            }
//            ret.setCode(stringBuffer.toString());
//            addContextualProperty(ret, ctx);
//            addNodeAndPreEdge(ret);
//            dontPop = true;
//            return null;
//        }

//        /**
//         * 不用单独处理形参列表中带有赋值的参数
//         * @param ctx
//         * @return
//         */
//        @Override
//        public Void visitFormalParameterArg(JavaScriptParser.FormalParameterArgContext ctx) {
//            //    : assignable ('=' singleExpression)?      // ECMAScript 6: Initialization
//            CFNode ret = new CFNode();
//            ret.setLineOfCode(ctx.getStart().getLine());
//            ret.setCode(getOriginalCodeText(ctx.assignable()) + "=" + getOriginalCodeText(ctx.singleExpression()));
//            addContextualProperty(ret, ctx);
//            addNodeAndPreEdge(ret);
//            dontPop = true;
//            return null;
//        }

//        @Override
//        public Void visitLastFormalParameterArg(JavaScriptParser.LastFormalParameterArgContext ctx) {
//            CFNode ret = new CFNode();
//            ret.setLineOfCode(ctx.getStart().getLine());
//            ret.setCode(getOriginalCodeText(ctx.singleExpression()));
//            addContextualProperty(ret, ctx);
//            addNodeAndPreEdge(ret);
//            dontPop = true;
//            return null;
//        }

        @Override
        public Void visitFunctionBody(JavaScriptParser.FunctionBodyContext ctx) {
//            CFNode ret = new CFNode();
//            ret.setLineOfCode(ctx.getStart().getLine());
//            ret.setCode(getOriginalCodeText(ctx.sourceElements()));
//            addContextualProperty(ret, ctx);
//            addNodeAndPreEdge(ret);
//            dontPop = true;

            visit(ctx.sourceElements());
            return null;
        }



        /**
         * Get resulting Control-Flow-Graph of this CFG-Builder.
         */
        public ControlFlowGraph getCFG() {
            return cfg;
        }


        /**
         * Get the original program text for the given parser-rule context.
         * This is required for preserving whitespaces.
         */
        private String getOriginalCodeText(ParserRuleContext ctx) {
            int start = ctx.start.getStartIndex();
            int stop = ctx.stop.getStopIndex();
            Interval interval = new Interval(start, stop);
            return ctx.start.getInputStream().getText(interval);
        }


        private void addNodeAndPreEdge(CFNode node) {
            cfg.addVertex(node);
            popAddPreEdgeTo(node);
        }

        /**
         * Add a new edge to the given node, by poping the edge-type of the stack.
         */
        private void popAddPreEdgeTo(CFNode node) {
            if (dontPop)
                dontPop = false;
            else {
                Logger.debug("\nPRE-NODES = " + preNodes.size());
                Logger.debug("PRE-EDGES = " + preEdges.size() + '\n');
                if (preNodes.size() > 0) {
                    cfg.addEdge(new Edge<>(preNodes.pop(), new CFEdge(preEdges.pop()), node));
                }
            }
            //
//            for (int i = casesQueue.size(); i > 0; --i)
//                cfg.addEdge(new Edge<>(casesQueue.remove(), new CFEdge(CFEdge.Type.TRUE), node));
            if (!casesQueue.isEmpty()) {
                // 与casesQueue最后一个case连接
                cfg.addEdge(new Edge<>(casesQueue.remove(), new CFEdge(CFEdge.Type.TRUE), node));
            }
        }
        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        /**
         * A simple structure for holding the start, end, and label of code blocks.
         * These are used to handle 'break' and 'continue' statements.
         */

        private class Block {

            public final String label;
            public final CFNode start, end;

            Block(CFNode start, CFNode end, String label) {
                this.start = start;
                this.end = end;
                this.label = label;
            }

            Block(CFNode start, CFNode end) {
                this(start, end, "");
            }
        }
    }
}
